﻿using EFFC.Frame.Net.Base.Data.Base;
using EFFC.Frame.Net.Module.Extend.EWRA.Logic;
using EFFC.Frame.Net.Resource.SQLServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EFFC.Extends.LinqDLR2SQL;
using EFFC.Frame.Net.Base.Common;
using EFFC.Frame.Net.Base.Data;
using EFFC.Frame.Net.Unit.DB;
using EFFC.Frame.Net.Unit.DB.Parameters;
using EFFC.Frame.Net.Base.ResouceManage.DB;
using EFFC.Frame.Net.Module.Business.Logic;
using EFFC.Frame.Net.Module.Extend.EWRA.DataCollections;
using EFFC.Frame.Net.Module.Extend.EWRA.Parameters;
using EFFC.Frame.Net.Resource.Sqlite;
using EFFC.Frame.Net.Base.Constants;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using EFFC.Frame.Net.Global;
using System.Security.Cryptography;
using System.IO;
using System.Text;
using ICPFrameLib.Common;
using System.Data.Common;

namespace XHRPMIS.Business
{
     public class MyAuth : AuthorizationLogic
{
    MyAuthHelper _db = null;

    public new MyAuthHelper DB
    {
        get
        {
            if (_db == null) _db = new MyAuthHelper(this);
            return _db;
        }
    }
    static object lockobj = new object();
    public override TimeSpan Expire => TimeSpan.FromHours(12);

    protected override bool DoLogin(string id)
    {
        var up = DB.NewMetaUnitParameter();
        var pw = ComFunc.AESEncrypt(ComFunc.nvl(PostDataD.pw));
        if (string.IsNullOrEmpty(id)) id = ComFunc.UrlDecode(ComFunc.nvl(PostDataD.id).ToLower());
        var s = from t in DB.LamdaTable(up, "EXTEND_LOGIN", "a")
                where t.loginid == id
                select t;
        BeginTrans();
        lock (lockobj)
        {
            var list = s.GetQueryList(up);
            if (list.Count != 1) return false;
            dynamic logininfo = list.First();
            if (logininfo.loginpass != pw)
            {
                DB.QuickUpdate(up, "EXTEND_LOGIN", new
                {
                    ErrorTime = IntStd.IsNotIntThen(logininfo.ErrorTime) + 1,
                    LastLoginDate = DateTime.Now,
                    LastLoginIP = ClientInfo.IP
                }, new { uid = logininfo.uid });

                return false;
            }


            DB.QuickUpdate(up, "EXTEND_LOGIN", new
            {
                ErrorTime = 0,
                LastLoginDate = DateTime.Now,
                LastLoginIP = ClientInfo.IP
            }, new { uid = logininfo.uid });
            //系统角色列表
            var roles = (from t in DB.LamdaTable(up, "EXTEND_ROLE_LOGIN", "a")
                         join t2 in DB.LamdaTable(up, "EXTEND_ROLE", "b") on t.RoleNo equals t2.RoleNo
                         where t.LoginUID == logininfo.uid
                         select new
                         {
                             t2.RoleNo,
                             t2.RoleName
                         }).GetQueryList(up);
            var role_str = string.Join(",", roles.Select(d => ComFunc.nvl(d.GetValue("RoleNo"))).ToArray());
            var role_name_str = string.Join(",", roles.Select(d => ComFunc.nvl(d.GetValue("RoleName"))).ToArray());
            //登录者的授权功能列表
            var functionlist = (from t in DB.LamdaTable(up, "EXTEND_ROLE_LOGIN", "a")
                                join t2 in DB.LamdaTable(up, "EXTEND_ROLE_FUNCTION", "b") on t.roleno equals t2.roleno
                                join t3 in DB.LamdaTable(up, "EXTEND_FUNCTION", "c") on t2.functionno equals t3.functionno
                                where t.loginuid == logininfo.uid
                                select new
                                {
                                    action = t3.action
                                }).Distinct().GetQueryList(up);
            var f_str = "";
            foreach (var f in functionlist)
            {
                f_str += $"{f.GetValue("action")},";
            }
            f_str = f_str == "" ? "" : f_str.Substring(0, f_str.Length - 1);
            #region 固定栏位
            //登录者的唯一编码
            SetClaimSaveParameter("Login_UID", logininfo.uid);
            SetClaimSaveParameter("Auth_Actions", f_str);
            //头像url
            SetClaimSaveParameter("avatar", logininfo.avatar);
            //用户类型
            SetClaimSaveParameter("usertype", logininfo.usertype);
            //关联业务资料pk
            SetClaimSaveParameter("userno", logininfo.userno);
            //登录姓名
            SetClaimSaveParameter("name", ComFunc.UrlEncode(logininfo.loginname));
            //用户业务资料记录的姓名
            SetClaimSaveParameter("username", ComFunc.UrlEncode(logininfo.loginname));
            //登录角色编号，多个逗号分隔
            SetClaimSaveParameter("roleno", role_str);
            //登录角色名称，多个逗号分隔
            SetClaimSaveParameter("rolename", ComFunc.UrlEncode(role_name_str));
            //用户的加密密钥
            var nonce_str = ComFunc.RandomString(8, true, true, true, false);
            //随机串，用于加密密钥
            SetClaimSaveParameter("login_sk", nonce_str);
            var key = ComFunc.RandomString(32, true, true, true, false);
            SetClaimSaveParameter("userk", JsCryptography.AES_Encrypt(key, nonce_str));
            var privateinfo = FrameDLRObject.CreateInstance();
            privateinfo.pw = logininfo.loginpass;
            privateinfo.mobile = logininfo.mobile;
            privateinfo.qq = logininfo.qq;
            var privateinfo_rsa_content = EncryptByPublicKey(privateinfo.ToJSONString());
            SetClaimSaveParameter("p_info", privateinfo_rsa_content);
            #endregion
        }
        CommitTrans();
        return true;
    }
    protected override bool IsValid(string token, ref string msg)
    {
        var rtn = base.IsValid(token, ref msg);
        GlobalCommon.Logger.WriteLog(LoggerLevel.INFO, msg);
        return rtn;
    }
    protected override bool IsValid_Level2(string token, FrameDLRObject payloadinfo, ref string msg)
    {
        var is_kickout = BoolStd.IsNotBoolThen(MyConfig.GetConfiguration("Server", "IsKickOut"), true);
        if (is_kickout)
            return base.IsValid_Level2(token, payloadinfo, ref msg);
        else
            return true;
    }
    /// <summary>
    /// 做自定义payload解析
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    protected override FrameDLRObject GetPayLoadInfoFromToken(string token)
    {
        var rtn = base.GetPayLoadInfoFromToken(token);
        //如果roleno为空或没有本系统中的角色，则设置为默认的R900(普通用户)
        if (ComFunc.nvl(rtn.GetValue("roleno")) == "")
        {
            rtn.SetValue("roleno", "R900");
        }
        if (ComFunc.nvl(rtn.GetValue("username")) != "")
        {
            rtn.SetValue("username", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("username"))));
        }
        if (ComFunc.nvl(rtn.GetValue("name")) != "")
        {
            rtn.SetValue("name", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("name"))));
        }
        if (ComFunc.nvl(rtn.GetValue("rolename")) != "")
        {
            rtn.SetValue("rolename", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("rolename"))));
        }
        if (ComFunc.nvl(rtn.GetValue("major_name")) != "")
        {
            rtn.SetValue("major_name", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("major_name"))));
        }
        if (ComFunc.nvl(rtn.GetValue("dept_name")) != "")
        {
            rtn.SetValue("dept_name", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("dept_name"))));
        }
        if (ComFunc.nvl(rtn.GetValue("class_name")) != "")
        {
            rtn.SetValue("class_name", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("class_name"))));
        }
        if (ComFunc.nvl(rtn.GetValue("sex")) != "")
        {
            rtn.SetValue("sex", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("sex"))));
        }
        if (ComFunc.nvl(rtn.GetValue("work")) != "")
        {
            rtn.SetValue("work", ComFunc.UrlDecode(ComFunc.nvl(rtn.GetValue("work"))));
        }
        //密钥的获取方法：jwt中获取login_sk和userk，login_sk做为key，将userk解密成真正的key
        //加密算法：需要加密的时候，先生成一个随机串，然后添加到原文的末尾，用#$2#分割，效果就是：{原文}#$2#{随机串}，然后做加密，随机串用nonce_str做参数传入
        var login_sk = ComFunc.nvl(rtn.GetValue("login_sk"));
        var userk = ComFunc.nvl(rtn.GetValue("userk"));
        var js_share_key = JsCryptography.ASE_Decrypt(userk, login_sk);
        rtn.SetValue("js_share_key", js_share_key);

        var privateinfo_rsa = ComFunc.nvl(rtn.GetValue("p_info"));
        if (privateinfo_rsa != "")
        {
            var privateinfo_json = DecryptByPublicKey(privateinfo_rsa);
            FrameDLRObject dinfo = FrameDLRObject.CreateInstance(privateinfo_json);
            foreach (var item in dinfo.Items)
            {
                rtn.SetValue(item.Key, item.Value);
            }
        }
        return rtn;
    }
    /// <summary>
    /// 通过公钥做key进行加密,AES算法
    /// </summary>
    /// <param name="text"></param>
    /// <returns></returns>
    private string EncryptByPublicKey(string text)
    {
        if (string.IsNullOrEmpty(text)) return null;
        if (!File.Exists(PublicKeySavePath.Replace("~", CallContext_Parameter.ServerRootPath))) return null;

        var content = File.ReadAllText(PublicKeySavePath.Replace("~", CallContext_Parameter.ServerRootPath));
        var key = ComFunc.getMD5_String(ComFunc.Base64Code(content));


        Byte[] toEncryptArray = Encoding.UTF8.GetBytes(text);

        System.Security.Cryptography.RijndaelManaged rm = new System.Security.Cryptography.RijndaelManaged
        {
            Key = Encoding.UTF8.GetBytes(key),
            Mode = System.Security.Cryptography.CipherMode.ECB,
            Padding = System.Security.Cryptography.PaddingMode.PKCS7
        };

        System.Security.Cryptography.ICryptoTransform cTransform = rm.CreateEncryptor();
        Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

        return Convert.ToBase64String(resultArray, 0, resultArray.Length);
    }
    /// <summary>
    /// 通过公钥做key进行解密,AES算法
    /// </summary>
    /// <param name="text"></param>
    /// <returns></returns>
    private string DecryptByPublicKey(string text)
    {
        if (string.IsNullOrEmpty(text)) return null;

        var content = File.ReadAllText(PublicKeySavePath.Replace("~", CallContext_Parameter.ServerRootPath));
        var key = ComFunc.getMD5_String(ComFunc.Base64Code(content));

        Byte[] toEncryptArray = Convert.FromBase64String(text);

        System.Security.Cryptography.RijndaelManaged rm = new System.Security.Cryptography.RijndaelManaged
        {
            Key = Encoding.UTF8.GetBytes(key),
            Mode = System.Security.Cryptography.CipherMode.ECB,
            Padding = System.Security.Cryptography.PaddingMode.PKCS7
        };

        System.Security.Cryptography.ICryptoTransform cTransform = rm.CreateDecryptor();
        Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

        return Encoding.UTF8.GetString(resultArray);
    }
    public override string Issuer => "Your_Issuer";
    public override string Audience => "Your_Audience";
    public override string PublicKeySavePath => "~/token/public.json";
    public override string PrivateKeySavePath => "~/token/private.json";

    public class MyAuthHelper : MyDBHelper
    {
        MyAuth _logic = null;

        public MyAuthHelper(MyAuth logic)
            : base(logic)
        {
            _logic = logic;
        }
        /// <summary>
        /// 创建一个默认的DB访问参数
        /// </summary>
        /// <returns></returns>
        public override UnitParameter NewDBUnitParameter()
        {
            var dbtype = ComFunc.nvl(_logic.Configs["DB_Type"]);
            UnitParameter rtn = null;
            if (dbtype.ToLower() == "sqlserver")
            {
                rtn = base.NewDBUnitParameter<SQLServerAccess>();
            }
            else if (dbtype.ToLower() == "mysql")
            {
                rtn = base.NewDBUnitParameter<MySQLAccess>();
            }
            else if (dbtype.ToLower() == "oracle")
            {
                rtn = base.NewDBUnitParameter<OracleAccess>();
            }
            else if (dbtype.ToLower() == "sqlite")
            {
                rtn = base.NewDBUnitParameter<SqliteAccess>();
            }
            rtn.Dao.Open(_logic.CallContext_Parameter.DBConnectionString);
            return rtn;
        }
        /// <summary>
        /// 创建一个MetaData的DB访问参数
        /// </summary>
        /// <returns></returns>
        public UnitParameter NewMetaUnitParameter()
        {
            var dbtype = ComFunc.nvl(_logic.Configs["MetaDB_Type"]);
            UnitParameter rtn = null;
            if (dbtype.ToLower() == "sqlserver")
            {
                rtn = base.NewDBUnitParameter<SQLServerAccess>();
            }
            else if (dbtype.ToLower() == "mysql")
            {
                rtn = base.NewDBUnitParameter<MySQLAccess>();
            }
            else if (dbtype.ToLower() == "oracle")
            {
                rtn = base.NewDBUnitParameter<OracleAccess>();
            }
            else if (dbtype.ToLower() == "sqlite")
            {
                rtn = base.NewDBUnitParameter<SqliteAccess>();
            }
            rtn.Dao.Open(ComFunc.nvl(_logic.CallContext_Parameter[DomainKey.CONFIG, "MetaConnection"]));
            return rtn;
        }
        /// <summary>
        /// 判断一个table是否已经存在
        /// </summary>
        /// <param name="up"></param>
        /// <param name="tablename"></param>
        /// <returns></returns>
        public bool IsTableExists(UnitParameter up, string tablename)
        {
            try
            {
                var i = (from t in LamdaTable(up, tablename, "a") where new LinqDLR2SqlWhereOperator("1=0", null) select t).Take(1).Count(up);
                return true;
            }
            catch (DbException ex)
            {
                return false;
            }
        }
    }
}
}
